﻿Friend Class HashProgressStream
    Inherits System.IO.Stream

    Private mStream As System.IO.Stream
    Private mFilePath As String

    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        MyBase.Dispose(disposing)
        Manager.OnHashFinished(mFilePath)
    End Sub

    Public Sub New(ByVal stream As System.IO.Stream, ByVal filePath As String)
        mStream = stream
        mFilePath = filePath
    End Sub

    Public Overrides ReadOnly Property CanRead() As Boolean
        Get
            Return mStream.CanRead
        End Get
    End Property

    Public Overrides ReadOnly Property CanSeek() As Boolean
        Get
            Return mStream.CanSeek
        End Get
    End Property

    Public Overrides ReadOnly Property CanWrite() As Boolean
        Get
            Return mStream.CanWrite
        End Get
    End Property

    Public Overrides Sub Flush()
        mStream.Flush()
    End Sub

    Public Overrides ReadOnly Property Length() As Long
        Get
            Return mStream.Length
        End Get
    End Property

    Public Overrides Property Position() As Long
        Get
            Return mStream.Position
        End Get
        Set(ByVal value As Long)
            mStream.Position = value
        End Set
    End Property

    Private mLastPercent As Integer = -1
    Private mLastPosition As Long = 0
    Private mLastTime As Long = 0
    Public Overrides Function Read(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer) As Integer
        Dim lLen As Long = mStream.Length
        Dim lPos As Long = mStream.Position
        Dim iPercent As Integer
        If lLen > 0 Then
            iPercent = CInt(100 * lPos / lLen)
        Else
            iPercent = 100
        End If
        If iPercent = -1 Then
            mLastPosition = mStream.Position
            mLastTime = Framework.Performance.Counter.GetPerfCounter
            mLastPercent = 0
            Manager.OnHashProgress(mFilePath, iPercent, 0)
        ElseIf iPercent <> mLastPercent Then
            Dim lCurrentTime As Long = Framework.Performance.Counter.GetPerfCounter
            Dim iMillisecs As Integer = Math.Max(1, Framework.Performance.Counter.GetTime(mLastTime, lCurrentTime))
            Dim iBytesPerSecond As Integer = CInt((mStream.Position - mLastPosition) / (iMillisecs / 1000))
            Manager.OnHashProgress(mFilePath, iPercent, iBytesPerSecond)
            mLastPercent = iPercent
            mLastTime = Framework.Performance.Counter.GetPerfCounter
            mLastPosition = mStream.Position
        End If
        Return mStream.Read(buffer, offset, count)
    End Function

    Public Overrides Function Seek(ByVal offset As Long, ByVal origin As System.IO.SeekOrigin) As Long
        Return mStream.Seek(offset, origin)
    End Function

    Public Overrides Sub SetLength(ByVal value As Long)
        mStream.SetLength(value)
    End Sub

    Public Overrides Sub Write(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer)
        mStream.Write(buffer, offset, count)
    End Sub
End Class

Friend Class HashHelper
    Public Shared Function CalcSHA512(ByVal filePath As String) As String
        Using fs As New IO.FileStream(filePath, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Delete Or IO.FileShare.ReadWrite)
            Using ps As New HashProgressStream(fs, filePath)
                Return Framework.Hash.SHA512.Compute(ps)
            End Using
        End Using
    End Function

    Public Shared Function FindSHA512InDataStore(ByVal fileName As String, ByVal fileSize As Long, ByVal sha512FirstMb As String, ByVal sha512LastMb As String) As String
        For Each clsFile As Entities.SharedItemFile In EntityHandlers.SharedItemsFiles.GetAllBySize(fileSize)
            If clsFile.SHA512HashForFirstMb = sha512FirstMb AndAlso clsFile.SHA512HashForLastMb = sha512LastMb Then
                Return clsFile.SHA512Hash
            End If
        Next
        Return Nothing
    End Function

    Public Shared Function CalcSHA512ForFirstMb(ByVal filePath As String) As String
        Using fs As New IO.FileStream(filePath, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
            Dim iLen As Integer = CInt(Math.Min(fs.Length, 1024 * 1024))
            Dim bBuf(iLen - 1) As Byte
            fs.Read(bBuf, 0, bBuf.Count)
            Using ms As New IO.MemoryStream(bBuf)
                Using ps As New HashProgressStream(ms, filePath)
                    Return Framework.Hash.SHA512.Compute(ps)
                End Using
            End Using
        End Using
    End Function
    Public Shared Function CalcSHA512ForLastMb(ByVal filePath As String) As String
        Using fs As New IO.FileStream(filePath, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
            Dim iLen As Integer = CInt(Math.Min(fs.Length, 1024 * 1024))
            fs.Seek(iLen, IO.SeekOrigin.End)
            Dim bBuf(iLen - 1) As Byte
            fs.Read(bBuf, 0, bBuf.Count)
            Using ms As New IO.MemoryStream(bBuf)
                Using ps As New HashProgressStream(ms, filePath)
                    Return Framework.Hash.SHA512.Compute(ps)
                End Using
            End Using
        End Using
    End Function

    Private Shared Function AreEqualArrays(ByVal arr1() As Byte, ByVal arr2() As Byte) As Boolean
        If arr1 Is Nothing Then
            Return arr2 Is Nothing
        ElseIf arr2 Is Nothing Then
            Return False
        ElseIf arr1.Count <> arr2.Count Then
            Return False
        Else
            For i As Integer = 0 To arr1.Count - 1
                If arr1(i) <> arr2(i) Then Return False
            Next
            Return True
        End If
    End Function

End Class